package xyz.labmem.main.safety;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import xyz.labmem.main.annotation.RoleValid;
import xyz.labmem.main.constants.ConstantsRedis;
import xyz.labmem.main.constants.ConstantsRole;
import xyz.labmem.main.application.entity.sys.User;
import xyz.labmem.main.enums.StateEnum;
import xyz.labmem.main.result.ResultUtil;
import xyz.labmem.main.sys.StaticVariable;
import xyz.labmem.main.tool.*;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.KeySpec;
import java.util.List;
import java.util.Objects;

/**
 * AOP权限获取验证
 * Created in 22:24 2020/3/10
 *
 * @author Lty
 */
@Slf4j
@Aspect
@Component
public class RoleAspect extends StaticVariable {

    private final Redis redis;

    private boolean isAPI = true;

    public RoleAspect(Redis redis) {
        this.redis = redis;
    }

    private Object roleFailedObject;

    /**
     * Spring中使用@Pointcut注解来定义方法切入点
     *
     * @Pointcut 用来定义切点，针对方法
     * @Aspect 用来定义切面，针对类 后面的增强均是围绕此切入点来完成的
     * 此处仅配置被我们刚才定义的注解：RoleValid修饰的方法即可
     */
    @Pointcut("@annotation(roleValid)")
    public void doAuthToken(RoleValid roleValid) {
    }

    /**
     * 此处我使用环绕增强，在方法执行之前或者执行之后均会执行。
     */
    @Around(value = "doAuthToken(roleValid)", argNames = "pjp,roleValid")
    public Object auth(ProceedingJoinPoint pjp, RoleValid roleValid) throws Throwable {
        //获取切面目标的对象
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        //获取切面的类和方法
        String classPath = targetMethod.getDeclaringClass().getName();
        String methodName = targetMethod.getName();
        //获取对象是否是APIController
        isAPI = targetMethod.getDeclaringClass().isAnnotationPresent(RestController.class);

        //====进行api权限比对====

        //获取接口权限列表
        List<String> roleList = getRoleList(classPath + "." + methodName);
        if (roleList == null || roleList.size() == 0) {
            verification(null);
            return pjp.proceed();
        } else {
            if (verification(roleList))
                return pjp.proceed();
            else
                return roleFailedObject;
        }
    }

    private boolean verification(List<String> roleList) {
        //====进行token的权限校验====
        //获取RequestHeader中的token
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 获取access_token
        String access_token = request.getHeader(ConstantsRole.REQUEST_HEADER_TOKEN_KEY);
        //如果RequestHeader没有token就从cookie中获取
        if (StrUtil.isEmpty(access_token)) {
            access_token = CookieUtils.getCookie(request, ConstantsRole.REQUEST_HEADER_TOKEN_KEY);
            if (StrUtil.isEmpty(access_token)) {
                //没有token信息的连接，返回失败
                roleFailed("请先登录");
                return false;
            }
        }

        //====进行用户的权限验证====
        String keyStr = redis.get(ConstantsRedis.TOKEN_PREFIX + access_token);

        //redis 用户验证过期
        if (StrUtil.isEmpty(keyStr)) {
            roleFailed("请先登录");
            return false;
        }

        String userJson = JWT.getValueByKeyStr(access_token, keyStr);
        //JWT 用户验证过期
        if (StrUtil.isEmpty(userJson)) {
            roleFailed("登录超期，请重新登录");
            return false;
        }
        User user = Jackson.JsonToObject(userJson, User.class);
        //序列化效验
        if (user == null) {
            roleFailed("验证失败，请重新登录");
            return false;
        } else {
            if (roles.get(user.getRole()).getState() != StateEnum.ON) {
                roleFailed("账号权限失效");
                return false;
            }
        }
        if (roleList == null || user.getIsAdmin().equals("2")) {
            user.setToken(access_token);
            StaticVariable.saveUser(user);
            return true;
        } else {
            //检验用户对api的权限`
            for (String role : roleList) {
                if (role.equals(user.getRole())) {
                    user.setToken(access_token);
                    StaticVariable.saveUser(user);
                    return true;
                }
            }
            roleFailed("您的权限不正确");
            return false;
        }
    }

    private void roleFailed(String message) {
        if (isAPI) {
            roleFailedObject = ResultUtil.userFailed(message);
        } else {
            roleFailedObject = "redirect:/login";
        }
    }


}
